export * from "./message-helpers";
export * from "./data-transformers";
export * from "./constants";
export * from "./auth";

import type Konva from "konva";

import {
  T_GraphsOptions,
  T_GraphsConfigItem2,
  T_GraphsOptionsSet,
  T_KonvaGroupConfig,
} from "@/types/graphs-konva.ts";

import type { T_SocketPipelineData } from "@/pages/event-warn/types";
import {
  T_RectGraphResultItem,
  T_Device_ClientSide_Cam_NodeDetails,
  // T_Camera_Video_Row,
  // rectGraphResultItem,
  // T_RectDelayListItem,
} from "@/pages/components/types.ts";

import {
  CAMERA_STREAM_NULL,
  KONVA_BORDER_COLOR_RED,
  CAMERA_STREAM_ID,
} from "./constants";

//匹配http或者 https
export function isExternal(path: string) {
  return /^(https?:|mailto:|tel:)/.test(path);
}

/** 字符串中的数字会转换成“真正的”数字。另，结果为 NaN 。 */
export function getNumberSafely(input?: unknown): number {
  if (typeof input === "number") {
    return input;
  }
  if (typeof input === "string") {
    if (input.trim()) {
      return +input;
    }
  }
  return NaN;
}

export function formatStreamList(streamList: string[]) {
  let uiStreamList = [];
  const apiStreamList = streamList.map((url) => {
    const stream = url.split("-")[1];
    const name = stream.includes("raw") ? "原始画面" : stream;
    return {
      label: name,
      value: `/whep/endpoint/${url}`,
    };
  });
  const defaultList = [
    {
      label: "- -",
      value: CAMERA_STREAM_NULL,
    },
  ];
  uiStreamList =
    apiStreamList && apiStreamList.length
      ? [...defaultList, ...apiStreamList]
      : defaultList;
  return uiStreamList;
}

export function formatPipeList(pipeList: string[]) {
  let uiStreamList = [];
  const apiStreamList = pipeList.map((pipe) => {
    const stream = pipe.split("-")[1];
    const name = stream.includes("raw") ? "原始画面" : stream;
    return {
      label: name,
      value: name === "原始画面" ? "" : stream,
    };
  });
  const defaultList = [
    {
      label: "- -",
      value: CAMERA_STREAM_NULL,
    },
  ];
  uiStreamList =
    apiStreamList && apiStreamList.length
      ? [...defaultList, ...apiStreamList]
      : defaultList;
  return uiStreamList;
}

export function formatStreamIdList() {
  const defaultList = [
    {
      label: "- -",
      value: CAMERA_STREAM_NULL,
    },
    {
      label: "原始画面",
      value: "",
    },
    {
      label: "检测结果",
      value: CAMERA_STREAM_ID,
    },
  ];
  return defaultList;
}

export function generateUUID() {
  let d = new Date().getTime();
  if (window.performance && typeof window.performance.now === "function") {
    d += performance.now(); // use high-precision timer if available
  }
  const uuid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(
    /[xy]/g,
    function (c) {
      const r = (d + Math.random() * 16) % 16 | 0;
      d = Math.floor(d / 16);
      return (c === "x" ? r : (r & 0x3) | 0x8).toString(16);
    }
  );
  return uuid;
}

const rectStrokeWidth = 5;
export function toRectConfig(options: T_GraphsOptions): Konva.RectConfig {
  const isGroupPart = options.isGroupPart || false;
  return {
    x: isGroupPart ? 0 : options.left,
    y: isGroupPart ? 0 : options.top,
    width: options.width,
    height: options.height,
    fill: "rgba(0,0,0,0)",
    stroke: options.color,
    strokeWidth: rectStrokeWidth,
    uiType: "rectBoxTarget",
    name: options.class_name || options.class_id || "rectGraph",
  };
}
export function toRectLabelConfig(options: T_GraphsOptions): Konva.TextConfig {
  const isGroupPart = options.isGroupPart || false;
  return {
    text: options.class_name || options.class_id || "rectGraph",
    textSubInfo: options.class_name_title || "",
    padding: 1,
    fontSize: 12,
    height: 12,
    shadowColor: "rgba(0, 0, 0, 1)",
    shadowBlur: 10,
    fill: "#fff",
    align: "left",
    x: isGroupPart ? 0 : options.left,
    y: isGroupPart ? 0 : options.top,
    strokeWidth: rectStrokeWidth,
    uiType: "labelText",
    uiDirection: options.uiDirection || "top",
  };
}
export function toRectLabelBgConfig(
  options: T_GraphsOptions
): Konva.RectConfig {
  const isGroupPart = options.isGroupPart || false;
  return {
    x: isGroupPart ? 0 : options.left,
    y: isGroupPart ? 0 : options.top,
    width: options.width,
    height: 14,
    fill: options.color,
    strokeWidth: rectStrokeWidth,
    name: options.class_name || options.class_id || "rectLabelBg",
    uiDirection: options.uiDirection || "top",
  };
}
export function toRectGroupConfig(options: T_GraphsOptions): Konva.GroupConfig {
  return {
    x: options.left,
    y: options.top,
    width: options.width,
    height: options.height,
    name: `group-rect-${options.class_id}`,
    uiType: "rectGroup",
    id: `group-rect-${options.class_id}`,
    strokeWidth: rectStrokeWidth,
  };
}

export function toZebraLineConfig(
  options: T_GraphsOptionsSet<number>
): Konva.LineConfig {
  return {
    // x: options.left,
    // y: options.top,
    points: options.points,
    stroke: options.stroke || "green", // 根据y坐标切换颜色
    strokeWidth: 3,
    lineCap: "square",
    name: options.class_name || options.class_id || "roi-line",
    closed: true, // 将线闭合形成多边形
    dash: options.dash || [],
  };
}
export function coordinateFormat(options: {
  left: number;
  top: number;
  width: number;
  height: number;
  frame: {
    width: number;
    height: number;
  };
}): {
  scaleX: number;
  scaleY: number;
  scaleW: number;
  scaleH: number;
} {
  const { left, top, width, height, frame } = options;
  let scaleX = left / frame.width;
  let scaleY = top / frame.height;
  let scaleW = width / frame.width;
  let scaleH = height / frame.height;
  scaleX = scaleX ? +scaleX.toFixed(3) : 0;
  scaleY = scaleY ? +scaleY.toFixed(3) : 0;
  scaleW = scaleW ? +scaleW.toFixed(3) : 0;
  scaleH = scaleH ? +scaleH.toFixed(3) : 0;
  return {
    scaleX,
    scaleY,
    scaleW,
    scaleH,
  };
}
export function formatSocketDataToKonvaConfig(
  socketData: T_SocketPipelineData,
  sourceCameraList: T_Device_ClientSide_Cam_NodeDetails[]
): Array<T_Device_ClientSide_Cam_NodeDetails> | null {
  if (!socketData.camera_id) return null;
  const list = [...sourceCameraList];
  list?.forEach((item) => {
    if (item._id === socketData.camera_id) {
      const konvaList: T_GraphsConfigItem2[] = [];
      socketData.results.forEach((res) => {
        const {
          class_name,
          class_id,
          bbox: { left, top, height, width },
          frame,
          color,
        } = res;

        const coordinate = coordinateFormat({
          left,
          top,
          width,
          height,
          frame,
        });
        const graphColor = color
          ? `rgba(${color.join(",")})`
          : `rgba(${KONVA_BORDER_COLOR_RED})`;

        const graphOptions = {
          left: coordinate.scaleX,
          top: coordinate.scaleY,
          width: coordinate.scaleW,
          height: coordinate.scaleH,
          class_id: class_id || generateUUID(),
          class_name: class_name,
          color: graphColor,
          unconverted: {
            width,
            height,
            top,
            left,
            frame,
          },
        };

        const label = toRectLabelConfig(graphOptions);
        const labelBg = toRectLabelBgConfig(graphOptions);
        const rect = toRectConfig(graphOptions);

        if (res.bbox) {
          const rectConfig: T_GraphsConfigItem2 = {
            vueComponentName: "v-rect",
            konvaGraphConfig: rect,
          };
          const textBgConfig: T_GraphsConfigItem2 = {
            vueComponentName: "v-rect",
            konvaGraphConfig: labelBg,
          };
          const textConfig: T_GraphsConfigItem2 = {
            vueComponentName: "v-text",
            konvaGraphConfig: label,
          };
          konvaList.push(rectConfig, textBgConfig, textConfig);
        }
      });
      if (item.konvaConfigMap) {
        item.konvaConfigMap.set(socketData.pipeline_id, konvaList);
      }
    }
  });
  return list;
}

export function formatDataToKonvaConfig(
  sourceRectData: T_RectGraphResultItem[]
): T_GraphsConfigItem2[] | [] {
  if (!sourceRectData.length || !sourceRectData[0].bbox) return [];
  const konvaList: T_GraphsConfigItem2[] = [];
  // console.log(121, sourceRectData)
  sourceRectData.forEach((res) => {
    const {
      class_name,
      action_idx,
      class_id,
      bbox: { left, top, height, width },
      frame,
      color,
    } = res;
    const customFrame = frame ? frame : { width: 1920, height: 1080 };
    const coordinate = coordinateFormat({
      left,
      top,
      width,
      height,
      frame: customFrame,
    });
    const graphColor = color
      ? `rgba(${color.join(",")})`
      : `rgba(${KONVA_BORDER_COLOR_RED})`;

    const graphOptions = {
      left: coordinate.scaleX,
      top: coordinate.scaleY,
      width: coordinate.scaleW,
      height: coordinate.scaleH,
      class_id: class_id || generateUUID(),
      class_name: action_idx || class_name,
      color: graphColor,
      unconverted: {
        width,
        height,
        top,
        left,
        frame: customFrame,
      },
    };

    const label = toRectLabelConfig(graphOptions);
    const labelBg = toRectLabelBgConfig(graphOptions);
    const rect = toRectConfig(graphOptions);

    if (res.bbox) {
      const rectConfig: T_GraphsConfigItem2 = {
        vueComponentName: "v-rect",
        konvaGraphConfig: rect,
      };
      const textBgConfig: T_GraphsConfigItem2 = {
        vueComponentName: "v-rect",
        konvaGraphConfig: labelBg,
      };
      const textConfig: T_GraphsConfigItem2 = {
        vueComponentName: "v-text",
        konvaGraphConfig: label,
      };
      konvaList.push(rectConfig, textBgConfig, textConfig);
    }
  });
  return konvaList;
}

export const findBindName = (id: string, sourceList: any[]): string => {
  if (!sourceList.length) return "";

  const bindName = sourceList.find((item) => item._id.includes(id));

  return bindName?.bindName || "";
};

export function findClosestItem(array: any, target: number) {
  // 初始化最小差值为无穷大
  let minDifference = Infinity;
  // 初始化最接近的项为null
  let closestItem = null;

  // 遍历数组对象
  for (let item of array) {
    // 计算当前项与目标数字的差值
    let difference = Math.abs(item.timestamp - target);
    // 如果当前差值比最小差值小，则更新最小差值和最接近的项
    if (difference < minDifference && difference < 1000) {
      minDifference = difference;
      closestItem = item;
    }
  }

  // 返回具有最小差值的项
  return closestItem;
}

export function getKonvaListOfGroup(
  socketData: T_SocketPipelineData
): T_KonvaGroupConfig[] {
  const konvaList: T_KonvaGroupConfig[] = [];
  socketData.results.forEach((res, index) => {
    const {
      class_name,
      class_id,
      bbox: { left, top, height, width },
      confidence,
      color,
    } = res;

    // const coordinate = coordinateFormat({left, top, width, height, frame})
    const graphColor = color
      ? `rgba(${color.join(",")})`
      : `rgba(${KONVA_BORDER_COLOR_RED})`;
    const uiConfidence = confidence ? (confidence * 100).toFixed(2) : 0;
    const graphOptions = {
      left: left,
      top: top,
      width: width,
      height: height,
      class_id: class_id || generateUUID(),
      class_name: `${class_name}`,
      class_name_title: "",
      color: graphColor,
      isGroupPart: true,
    };

    const graphOptionsConfidence = {
      ...graphOptions,
      ...{
        class_name: `${uiConfidence}%`,
        class_name_title: "置信度:",
        uiDirection: "bottom",
      },
    };
    const label = toRectLabelConfig(graphOptions);
    const labelBg = toRectLabelBgConfig(graphOptions);
    const rect = toRectConfig(graphOptions);
    const group = toRectGroupConfig(graphOptions);
    const labelConfidence = toRectLabelConfig(graphOptionsConfidence);
    const labelBgConfidence = toRectLabelBgConfig(graphOptionsConfidence);

    if (res.bbox) {
      const rectConfig: T_GraphsConfigItem2 = {
        vueComponentName: "v-rect",
        konvaGraphConfig: rect,
      };
      const textBgConfig: T_GraphsConfigItem2 = {
        vueComponentName: "v-rect",
        konvaGraphConfig: labelBg,
      };
      const textConfig: T_GraphsConfigItem2 = {
        vueComponentName: "v-text",
        konvaGraphConfig: label,
      };
      const textBgConfigConfidence: T_GraphsConfigItem2 = {
        vueComponentName: "v-rect",
        konvaGraphConfig: labelBgConfidence,
      };
      const textConfigConfidence: T_GraphsConfigItem2 = {
        vueComponentName: "v-text",
        konvaGraphConfig: labelConfidence,
      };
      const rectGroupConfig: T_KonvaGroupConfig = {
        vueComponentName: "v-group",
        konvaGraphConfig: group,
        GraphsConfigItems: [
          rectConfig,
          textBgConfigConfidence,
          textConfigConfidence,
          textBgConfig,
          textConfig,
        ],
      };

      konvaList.push(rectGroupConfig);
    }
  });
  return konvaList || [];
}

//时间格式化函数
export function formatDate(timestamp: number | string): string {
  if (typeof timestamp === "string") {
    timestamp = parseInt(timestamp, 10);
  }

  if (typeof timestamp !== "number" || isNaN(timestamp)) {
    console.error("Invalid timestamp:", timestamp);
    return "Invalid Date";
  }

  const date = new Date(timestamp);
  if (!(date instanceof Date) || isNaN(date.getTime())) {
    console.error("Invalid date:", date);
    return "Invalid Date";
  }

  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, "0");
  const day = String(date.getDate()).padStart(2, "0");
  const hours = String(date.getHours()).padStart(2, "0");
  const minutes = String(date.getMinutes()).padStart(2, "0");
  const seconds = String(date.getSeconds()).padStart(2, "0");

  return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}

export function extractFileName(url: string): string {
  // 使用正则表达式匹配 .m4p 文件名
  const match = url.match(/([^&]+\.mp4)/);
  if (match && match[1]) {
    return match[1];
  }
  return "";
}

/**
 * 使用 JSON.parse 和 JSON.stringify 清除空对象
 *
 * @export
 * @param {Record<any, any>} obj
 * @return {*}  {Record<any, any>}
 */
export function cleanEmptyObjects(obj: Record<any, any>): Record<any, any> {
  return JSON.parse(
    JSON.stringify(obj, (key, value) => {
      const isEmptyObject =
        value && typeof value === "object" && !Object.keys(value).length;
      if (isEmptyObject || [null, ""].includes(value)) {
        return undefined; // 过滤空对象
      }
      return value;
    })
  );
}

/**
 * 格式化时间戳为字符串
 * @param timestamp 时间戳（毫秒）
 * @returns 格式化后的时间字符串 (YYYY-MM-DD HH:mm:ss)
 */
export function formatTimestamp(timestamp: number): string {
  if (!timestamp) return "";

  const date = new Date(timestamp);
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, "0"); // 月份从0开始，需要加1
  const day = String(date.getDate()).padStart(2, "0");
  const hours = String(date.getHours()).padStart(2, "0");
  const minutes = String(date.getMinutes()).padStart(2, "0");
  const seconds = String(date.getSeconds()).padStart(2, "0");

  return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}

/**
 * 检测矩形是否与多边形相交
 * @param rect 矩形 {x, y, width, height}
 * @param polygon 多边形点数组 [x1, y1, x2, y2, ...]
 */
export function isRectIntersectPolygon(
  rect: { x: number; y: number; width: number; height: number },
  polygon: number[]
): boolean {
  // 方法1：检查矩形的4个顶点是否在多边形内
  const rectPoints = [
    { x: rect.x, y: rect.y },
    { x: rect.x + rect.width, y: rect.y },
    { x: rect.x + rect.width, y: rect.y + rect.height },
    { x: rect.x, y: rect.y + rect.height },
  ];

  // 射线法判断点是否在多边形内
  function isPointInPolygon(point: { x: number; y: number }): boolean {
    let inside = false;
    for (let i = 0, j = polygon.length - 2; i < polygon.length; i += 2) {
      const xi = polygon[i];
      const yi = polygon[i + 1];
      const xj = polygon[j];
      const yj = polygon[j + 1];

      const intersect =
        yi > point.y !== yj > point.y &&
        point.x < ((xj - xi) * (point.y - yi)) / (yj - yi) + xi;
      if (intersect) inside = !inside;

      j = i;
    }
    return inside;
  }

  // 如果任一矩形顶点在多边形内，则相交
  if (rectPoints.some(isPointInPolygon)) {
    return true;
  }

  // 方法2：检查矩形边是否与多边形边相交
  function doLinesIntersect(
    p1: { x: number; y: number },
    p2: { x: number; y: number },
    p3: { x: number; y: number },
    p4: { x: number; y: number }
  ): boolean {
    const det = (p2.x - p1.x) * (p4.y - p3.y) - (p4.x - p3.x) * (p2.y - p1.y);
    if (det === 0) return false;

    const lambda =
      ((p4.y - p3.y) * (p4.x - p1.x) + (p3.x - p4.x) * (p4.y - p1.y)) / det;
    const gamma =
      ((p1.y - p2.y) * (p4.x - p1.x) + (p2.x - p1.x) * (p4.y - p1.y)) / det;

    return lambda > 0 && lambda < 1 && gamma > 0 && gamma < 1;
  }

  // 检查所有边的组合
  for (let i = 0; i < rectPoints.length; i++) {
    const rectP1 = rectPoints[i];
    const rectP2 = rectPoints[(i + 1) % rectPoints.length];

    for (let j = 0; j < polygon.length - 2; j += 2) {
      const polyP1 = { x: polygon[j], y: polygon[j + 1] };
      const polyP2 = {
        x: j + 2 < polygon.length ? polygon[j + 2] : polygon[0],
        y: j + 3 < polygon.length ? polygon[j + 3] : polygon[1],
      };

      if (doLinesIntersect(rectP1, rectP2, polyP1, polyP2)) {
        return true;
      }
    }
  }

  return false;
}

/**
 * 将类别名称哈希为整数，映射到 HSL 颜色空间
 */
export const stringToHash = (str: string): number => {
  let hash = 0;
  for (let i = 0; i < str.length; i++) {
    hash = str.charCodeAt(i) + ((hash << 5) - hash);
  }
  return hash;
};

/**
 * 根据类别名称哈希生成颜色
 * 相同类别名称会生成相同的颜色
 */
export const getColorByHash = (className: string): string => {
  // 特殊类别固定颜色
  if (className === "person") {
    return "hsl(120, 100%, 50%)"; // 人物固定为绿色
  }
  
  const hash = stringToHash(className);
  const hue = Math.abs(hash % 360); // 色相 0-360°
  const saturation = 70 + (hash % 30); // 饱和度 70-100%
  const lightness = 40 + (hash % 20); // 亮度 40-60%
  return `hsl(${hue}, ${saturation}%, ${lightness}%)`;
};

/**
 * 工具函数：将 HSL 颜色字符串转换为 RGB 数组
 */
export const hslToRgb = (h: number, s: number, l: number): [number, number, number] => {
  s /= 100;
  l /= 100;

  const c = (1 - Math.abs(2 * l - 1)) * s;
  const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
  const m = l - c / 2;

  let r = 0,
    g = 0,
    b = 0;

  if (0 <= h && h < 60) {
    [r, g, b] = [c, x, 0];
  } else if (60 <= h && h < 120) {
    [r, g, b] = [x, c, 0];
  } else if (120 <= h && h < 180) {
    [r, g, b] = [0, c, x];
  } else if (180 <= h && h < 240) {
    [r, g, b] = [0, x, c];
  } else if (240 <= h && h < 300) {
    [r, g, b] = [x, 0, c];
  } else if (300 <= h && h < 360) {
    [r, g, b] = [c, 0, x];
  }

  return [Math.round((r + m) * 255), Math.round((g + m) * 255), Math.round((b + m) * 255)];
};

/**
 * 高性能文本对比色计算（基于 W3C 快速对比算法）
 */
export const getContrastTextColor = (hslColor: string): string => {
  // 解析 HSL 颜色字符串
  const hslRegex = /hsl\((\d+),\s*(\d+)%,\s*(\d+)%\)/;
  const match = hslColor.match(hslRegex);

  if (!match) {
    console.warn("Invalid HSL color format, using default contrast color");
    return "#000000"; // 默认返回黑色
  }

  // 提取 HSL 值
  const h = parseInt(match[1], 10);
  const s = parseInt(match[2], 10);
  const l = parseInt(match[3], 10);

  // 转换为 RGB
  const [r, g, b] = hslToRgb(
    Math.max(0, Math.min(360, h)), // 限制色相范围
    Math.max(0, Math.min(100, s)), // 限制饱和度范围
    Math.max(0, Math.min(100, l)) // 限制亮度范围
  );

  // 计算相对亮度（简化版）
  const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;

  // 根据亮度返回黑色或白色文本
  return luminance > 0.5 ? "#000000" : "#FFFFFF";
};

/**
 * 根据情感类型返回情感颜色
 */
export const getMoodColor = (mood: string): string => {
  const moodColorMap: Record<string, string> = {
    happy: "hsl(120, 100%, 50%)", // 绿色
    sad: "hsl(240, 100%, 50%)", // 蓝色
    angry: "hsl(0, 100%, 50%)", // 红色
    surprised: "hsl(60, 100%, 50%)", // 黄色
    neutral: "hsl(0, 0%, 50%)", // 灰色
  };
  return moodColorMap[mood] || "hsl(0, 0%, 50%)";
};